﻿Imports log4net
Imports log4net.Config

Imports VPager.Settings
Imports VPager.Display
Imports VPager.WebService
Imports System.Net
Imports VPager.NetworkMonitor
Imports VPager.Enumerations

Public Class Controller

    Const DEF_SERVICE_PROTO As String = "http"
    Const DEF_SERVICE_PORT As Integer = 8000
    Const DEF_BASE_SERVICE_URI As String = "/PagingService"


    Private Shared sInstance As Controller
    Private Shared SYNC_OBJ = New Object()

    Private mServiceInfo As ServiceInfo = New ServiceInfo()
    Private mPagerDisplay As PagerDisplay
    Private mIsDisplayActive As Boolean = False
    Private mPageList As PageList
    Private mDisplayState As WindowState = WindowState.CLOSED
    Private mNetMonitor As NetworkMonitor
    Private mLog As ILog
    Dim mIsRunning As Boolean


    ' -------------------------------------------------------------------------
    ' Property Definitions
    ' -------------------------------------------------------------------------
    '
    Public ReadOnly Property isRunning As Boolean
        Get
            Return mIsRunning
        End Get
    End Property

    Public ReadOnly Property DisplayState As WindowState
        Get
            Return mDisplayState
        End Get
    End Property

    Public ReadOnly Property IsDisplayActive As Boolean
        Get
            Return mDisplayState = WindowState.LOADED
        End Get
    End Property

    Public ReadOnly Property PageList As PageList
        Get
            Return mPageList
        End Get
    End Property

    Public ReadOnly Property NetMonitor As NetworkMonitor
        Get
            Return mNetMonitor
        End Get
    End Property

    Public ReadOnly Property ServiceInfo As ServiceInfo
        Get
            Return mServiceInfo
        End Get
    End Property

    Public ReadOnly Property Display As PagerDisplay
        Get
            Dim disp As PagerDisplay = Nothing
            If Me.IsDisplayActive Then
                disp = mPagerDisplay
            End If
            Return disp
        End Get
    End Property

    ' -------------------------------------------------------------------------
    ' Event Definitions
    ' -------------------------------------------------------------------------
    '
    Public Event DisplayStateChanged(ByVal state As WindowState)

    Protected Sub New()

        mLog = LogManager.GetLogger(Me.GetType)

        mLog.Info("Pager System Started.  Version " & Application.AppVersion)

    End Sub


    Public Shared Function getInstance() As Controller
        SyncLock (SYNC_OBJ)
            If IsNothing(sInstance) Then
                sInstance = New Controller()
            End If
        End SyncLock

        Return sInstance

    End Function

    Public Sub startup()
        If (Not Me.isRunning) Then
            setupPageList()
            setupNetworkMonitor()
            startWebService()

#If DEBUG Then
            primePagePump()
#End If
            mIsRunning = True
        End If

    End Sub

    Public Shared Sub shutdown()
        If Not IsNothing(sInstance) Then
            sInstance.mLog.Info("Pager System shutting down.")

            SyncLock (SYNC_OBJ)
                sInstance.mPageList = Nothing
                sInstance.mNetMonitor.stopMonitoring()
                sInstance.mNetMonitor = Nothing
                PagerWebService.stopService()
                sInstance.mIsRunning = False
            End SyncLock

            sInstance.mLog.Info("Pager System has been shutdown.")
        End If

    End Sub

    Public Sub startPagerDisplay()
        If (mPagerDisplay Is Nothing) Then
            mPagerDisplay = New PagerDisplay()
            ' Add handler so that the Active Page List can be updated when the Pager Display window loads
            AddHandler mPagerDisplay.Loaded, AddressOf pagerDisplayLoadedHandler
            AddHandler mPagerDisplay.Closed, AddressOf pagerDisplayClosedHandler

            ' The Pager Display window can take a 1-2 seconds to load so dispatch that into a background thread
            ' so that our control window remains responsive
            mPagerDisplay.Dispatcher.BeginInvoke(New Action(AddressOf mPagerDisplay.Show))
            mDisplayState = WindowState.LOADING

            publishDisplayState()

        End If
    End Sub

    Public Function closePagerDisplay(ByVal forceClose As Boolean) As Boolean
        Dim wasClosed = True

        If Not IsNothing(mPagerDisplay) Then
            If (mPageList.isEmpty Or forceClose) Then
                mPagerDisplay.Close()
            Else
                wasClosed = False
            End If
        End If

        Return wasClosed

    End Function

    Private Sub setupPageList()
        mPageList = New PageList()
    End Sub

    Private Sub setupNetworkMonitor()
        ' Create and configure our NetworkMonitor
        mNetMonitor = New NetworkMonitor(
            SettingsManager.getValue(SettingKey.PING_FREQUENCY),
            SettingsManager.getValue(SettingKey.NET_ALARM_THRESHOLD))
        mNetMonitor.LanPingTargets = SettingsManager.getValue(SettingKey.PING_LAN_TARGETS)
        mNetMonitor.WanPingTargets = SettingsManager.getValue(SettingKey.PING_WAN_TARGETS)
        Dim t As Integer = SettingsManager.getValue(SettingKey.PING_TIMEOUT)
        mNetMonitor.PingTimeout = t * 1000 ' convert seconds to milliseconds

        AddHandler mNetMonitor.StatusChanged, AddressOf networkChangeHandler

        mNetMonitor.startMontoring()

        mLog.Info("Network Monitor activated.")
    End Sub

    Private Function startWebService() As Boolean
        Dim serverAddress As String = getIPAdrressToBind()
        Dim serviceURI As String = DEF_SERVICE_PROTO + "://" +
                                    serverAddress + ":" +
                                    DEF_SERVICE_PORT.ToString +
                                    DEF_BASE_SERVICE_URI
        'Resulting serviceURL should look somethign like: http://localhost:8000/PagingService

        Return PagerWebService.startService(serviceURI)

    End Function

    Private Function getIPAdrressToBind() As String
        Dim myHost As String = System.Net.Dns.GetHostName()
        Dim myIP As String = "localhost" ' Default if suitable IPv4 address not found
        Dim ipAddr As IPAddress

        For i As Integer = 0 To System.Net.Dns.GetHostEntry(myHost).AddressList.Length - 1
            ipAddr = System.Net.Dns.GetHostEntry(myHost).AddressList(i)
            If (Not ipAddr.IsIPv6LinkLocal) Then
                myIP = ipAddr.ToString()
                Exit For
            End If
        Next

        mLog.DebugFormat("Total of {0} IP Addresses found. Using: {1}",
                         System.Net.Dns.GetHostEntry(myHost).AddressList.Length, myIP)
        Return myIP

    End Function

    Private Sub pagerDisplayLoadedHandler(ByVal sender As Object, ByVal e As RoutedEventArgs)
        mDisplayState = WindowState.LOADED
        publishDisplayState()
    End Sub

    Private Sub pagerDisplayClosedHandler(ByVal sender As Object, ByVal e As EventArgs)
        mDisplayState = WindowState.CLOSED
        mPagerDisplay = Nothing
        publishDisplayState()
    End Sub

    Private Sub primePagePump()

        If (Controller.getInstance.PageList.isEmpty) Then
            With Controller.getInstance.PageList
                .requestPageAdd(New ParentPage With {.Code = "ABC"})
                .requestPageAdd(New ParentPage With {.Code = "123", .IsUrgent = True})
                .requestPageAdd(New ParentPage With {.Code = "007"})
            End With
            mLog.Debug("PageList primed with test pages.")
        End If

    End Sub


    Public Enum WindowState
        CLOSED = 1
        LOADING = 2
        LOADED = 4
    End Enum

    Private Sub publishDisplayState()
        ' Update internal status
        If Me.IsDisplayActive Then
            mServiceInfo.Status.DisplayState = AbledState.ENABLED
            mServiceInfo.Status.DisplayMode = mPagerDisplay.DisplayMode
        Else
            mServiceInfo.Status.DisplayState = AbledState.DISABLED
            mServiceInfo.Status.DisplayMode = MonitorMode.UNKNOWN
        End If

        ' Publish status externally
        RaiseEvent DisplayStateChanged(mDisplayState)
    End Sub

    Private Sub networkChangeHandler(ByVal sender As NetworkMonitor, ByVal e As NetworkMonitor.NetworkEventArgs)
        If e IsNot Nothing Then
            mServiceInfo.Status.NetworkStatus = e.Status
        End If
    End Sub

End Class
